﻿using System;

using Microsoft.Xna.Framework;

namespace xEngine.DataTypes
{
    public class BoundingShape
    {
        #region Member Variables

        #endregion

        #region Properties

        public BoundingBox Box { get; private set; }
        public BoundingSphere Sphere { get; private set; }
        public BoundingFrustum Frustum { get; private set; }
        public BoundingType BoundingType { get; private set; }

        #endregion

        #region Constructors

        public BoundingShape()
        {
            BoundingType = BoundingType.None;
        }
        public BoundingShape(Vector3 min, Vector3 max)
        {
            Box = new BoundingBox(min, max);
            BoundingType = BoundingType.Box;
        }
        public BoundingShape(Vector3 center, float radius)
        {
            Sphere = new BoundingSphere(center, radius);
            BoundingType = BoundingType.Sphere;
        }
        public BoundingShape(Matrix viewProjection)
        {
            Frustum = new BoundingFrustum(viewProjection);
            BoundingType = BoundingType.Frustum;
        }

        #endregion

        #region Functions

        public IntersectionResult Intersects(Ray ray)
        {
            float? dist = null;

            switch (BoundingType)
            {
                case BoundingType.Box: dist = Box.Intersects(ray); break;
                case BoundingType.Sphere: dist = Sphere.Intersects(ray); break;
                case BoundingType.Frustum: dist = Frustum.Intersects(ray); break;
            }

            return new IntersectionResult(dist.HasValue, dist);
        }
        public IntersectionResult Intersects(BoundingBox box)
        {
            switch (BoundingType)
            {
                case BoundingType.Box: return new IntersectionResult(Box.Intersects(box));
                case BoundingType.Sphere: return new IntersectionResult(Sphere.Intersects(box));
                case BoundingType.Frustum: return new IntersectionResult(Frustum.Intersects(box));
            }

            return false;
        }
        public IntersectionResult Intersects(BoundingSphere sphere)
        {
            switch (BoundingType)
            {
                case BoundingType.Box: return new IntersectionResult(Box.Intersects(sphere));
                case BoundingType.Sphere: return new IntersectionResult(Sphere.Intersects(sphere));
                case BoundingType.Frustum: return new IntersectionResult(Frustum.Intersects(sphere));
            }

            return false;
        }
        public IntersectionResult Intersects(BoundingFrustum frustum)
        {
            switch (BoundingType)
            {
                case BoundingType.Box: return new IntersectionResult(Box.Intersects(frustum));
                case BoundingType.Sphere: return new IntersectionResult(Sphere.Intersects(frustum));
                case BoundingType.Frustum: return new IntersectionResult(Frustum.Intersects(frustum));
            }

            return false;
        }
        public IntersectionResult Intersects(BoundingShape shape)
        {
            switch (shape.BoundingType)
            {
                case BoundingType.Box: return Intersects(shape.Box);
                case BoundingType.Sphere: return Intersects(shape.Sphere);
                case BoundingType.Frustum: return Intersects(shape.Frustum);
            }

            return false;
        }

        #endregion  
    }

    public enum BoundingType
    {
        None,
        Box,
        Sphere,
        Frustum
    }

    public class IntersectionResult
    {
        public bool Intersects { get; private set; }
        public object Param { get; private set; }

        public IntersectionResult(bool intersects) : this(intersects, null) { }
        public IntersectionResult(bool intersects, object param)
        {
            Intersects = intersects;
            Param = param;
        }

        public static implicit operator IntersectionResult(bool b)
        {
            return new IntersectionResult(b);
        }
    }
}
